package version2;
import java.nio.FloatBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;

// Superduper Blitzklasse, Produktionsversion 1.0
// (LGPL) 2012 Robert Kühn

public class Blitz {
	public int MAXQUADS; // Maximale Quads
	
	public double def_startpunkt[];
	public double def_richtung[];
	public double def_spleisswahrsch;
	public int def_laenge;
	public double def_dicke;
	public double def_geolaenge;
	public float def_bias[];
	
	public double sphereradius=1;
	public boolean enableSpotlight=false;
	public int lightid=1;
	public double lightsource[]={0,0,0};
	
	public double quads[][][]; // quads[index][vertexindex][coordindex]
	public int quadCount;
	
	public Blitz(int maxquads)
	{
		MAXQUADS=maxquads;
		quads=new double[MAXQUADS][4][3];
		quadCount=0;
	}
	
	public void regenerate()
	{
		reset();
		generate(def_startpunkt.clone(),def_richtung.clone(),
				def_spleisswahrsch,def_laenge,def_dicke,def_geolaenge,
				def_bias.clone());
	}
	
	public void preset(double startpunkt[], double richtung[], double spleisswahrsch, int laenge, double dicke, double geolaenge, float bias[])
	{
		def_startpunkt=startpunkt; def_richtung=richtung; def_spleisswahrsch=spleisswahrsch;
		def_laenge=laenge; def_dicke=dicke; def_geolaenge=geolaenge;
		def_bias=bias;
		//lightsource=def_startpunkt.clone();
	}
	
	public void generate(double startpunkt[], double richtung[], double spleisswahrsch, int laenge, double dicke, double geolaenge, float bias[])
	{
		if ((laenge>0) && (quadCount<=MAXQUADS))
		{
			// Richtungsvektor zufällig verbiegen
			double richtungAbs=Math.sqrt(Math.pow(richtung[0], 2)+Math.pow(richtung[1],2)+Math.pow(richtung[2],2));
			double xrand,yrand,zrand;
			xrand=Math.random()*richtungAbs/4-richtungAbs/8+bias[0];
			yrand=Math.random()*richtungAbs/4-richtungAbs/8+bias[1];
			zrand=Math.random()*richtungAbs/4-richtungAbs/8+bias[2];
			richtung[0]=richtung[0]+=xrand; richtung[1]+=yrand; richtung[2]+=zrand;
			
			// Richtungsvektor normalisieren
			richtungAbs=Math.sqrt(Math.pow(richtung[0], 2)+Math.pow(richtung[1],2)+Math.pow(richtung[2],2));
			for (int i=0;i<3;i++) richtung[i]=richtung[i]/richtungAbs;
			
			// Zwei Vektoren erstellen, die dazu senkrecht sind
			// einer mit x-=1,y=0 und einer mit y=1,x=0
			double flaeche[][]=new double[2][3]; 
			flaeche[0][0]=1; flaeche[0][1]=0; flaeche[0][2]=-richtung[0]/richtung[2];
			flaeche[1][0]=0; flaeche[1][1]=1; flaeche[1][2]=-richtung[1]/richtung[2];
			
			// und dann noch normieren
			double flaecheAbs[]=new double[2];
			for (int i=0;i<2;i++)
			{
				double sum=0;
				for (int j=0;j<3;j++) sum+=flaeche[i][j]*flaeche[i][j];
				flaecheAbs[i]=Math.sqrt(sum);
			}
			for (int i=0;i<3;i++)
			{
				flaeche[0][i]/=flaecheAbs[0];
				flaeche[1][i]/=flaecheAbs[1];
			}
			
			// Länge bestimmen
			double segmentlaenge=geolaenge+((Math.random()*geolaenge/10)-geolaenge/20);
			
			// Linie konstruieren, an der wir unser Blitzsegment ausrichten
			double segmentachse[]=richtung;
			for (int i=0;i<3;i++) segmentachse[i]*=segmentlaenge;
			double endpunkt[]=addVectors(startpunkt,segmentachse,1);
			//for (int i=0;i<3;i++) endpunkt[i]+=segmentachse[i];
			
			// Konstruieren. Es gibt eine wichtige Reihenfolge für die Einfärbung
			// der Quad-Ecken: 1. Vertex weiß, 2. durchsichtig, 3. durchsichtig, 4. weiß.
			addQuad(startpunkt,
					addVectors(startpunkt,flaeche[0],-dicke),
					addVectors(endpunkt,flaeche[0],-dicke),
					endpunkt);
			addQuad(startpunkt,
					addVectors(startpunkt,flaeche[0],dicke),
					addVectors(endpunkt,flaeche[0],dicke),
					endpunkt);
			addQuad(startpunkt,
					addVectors(startpunkt,flaeche[1],-dicke),
					addVectors(endpunkt,flaeche[1],-dicke),
					endpunkt);
			addQuad(startpunkt,
					addVectors(startpunkt,flaeche[1],dicke),
					addVectors(endpunkt,flaeche[1],dicke),
					endpunkt);
			
			// Segment fertig. Nächstes Segment planen:
			float newbias[]={(float) -xrand*0.8f,(float) -yrand*0.8f,(float) -zrand*0.8f};
			generate(endpunkt.clone(),richtung.clone(),spleisswahrsch*0.95,laenge-1,dicke*0.9d,geolaenge*0.9d,newbias.clone());
			double spleissen=Math.random();
			if(spleissen<=spleisswahrsch)
			{
				for(int i=0;i<2;i++) newbias[i]=-newbias[i]*0.9f;
				generate(endpunkt.clone(),richtung.clone(),spleisswahrsch*0.7,laenge-2,dicke*0.7d,geolaenge*0.6d,newbias.clone());
			}
			if (enableSpotlight) 
				if (distance(endpunkt,def_startpunkt)<sphereradius+geolaenge) 
					if(distance(endpunkt,def_startpunkt)>sphereradius) 
						lightsource=endpunkt.clone();
		}
	}
	
	public void reset()
	{
		quadCount=0;
	}
	
	public void setLight(boolean enable, int id)
	{
		this.lightid=id;
		this.enableSpotlight=enable;
		if (enable)
		{
			FloatBuffer spec=BufferUtils.createFloatBuffer(4);
			spec.put(0.7f).put(0.7f).put(1f).put(1f).flip();
			GL11.glLight(GL11.GL_LIGHT0+lightid, GL11.GL_SPECULAR, spec);	
			GL11.glEnable(GL11.GL_LIGHT0+lightid);
			lightsource=def_startpunkt.clone();
		}
	}
	
	public void light()
	{
		FloatBuffer pos=BufferUtils.createFloatBuffer(4);		
		pos.put((float)(lightsource[0])).put((float)(lightsource[1])).put((float)(lightsource[2])).put(0.90f).flip();
		GL11.glLight(GL11.GL_LIGHT0+lightid, GL11.GL_POSITION,pos);
	}
	
	public void draw(){draw(1);}
	public void draw(double alpha){draw(alpha,true);}
	
	public void draw(double alpha, boolean lighting)
	{
		if(enableSpotlight) light();
		for(int quad=0;quad<quadCount;quad++)
		{
			GL11.glPushMatrix();
				if(!lighting) GL11.glDisable(GL11.GL_LIGHTING);
				GL11.glBegin(GL11.GL_QUADS);
					for(int vertex=0;vertex<4;vertex++)
					{
						switch(vertex)
						{
							case 1: case 2: GL11.glColor4d(0,0,0,0); break;
							default: GL11.glColor4d(0.7, 0.7, 1, alpha); break;	
						}
						GL11.glVertex3d(quads[quad][vertex][0],quads[quad][vertex][1],quads[quad][vertex][2]);
					}
				GL11.glEnd();
				if(!lighting) GL11.glEnable(GL11.GL_LIGHTING);
			GL11.glPopMatrix();
		}
		//if(enableSpotlight) light();
	}
	
	public void addQuad(double opaque1[],double transparent1[], double transparent2[], double opaque2[])
	{
		if(quadCount<MAXQUADS)
		{
			for (int i=0;i<4;i++)
			{
				double vertex[]={0,0,0};
	
				// Select vertex
				switch(i)
				{
					case 0: vertex=opaque1; break;
					case 1: vertex=transparent1; break;
					case 2: vertex=transparent2; break;
					case 3: vertex=opaque2; break;
				}
				
				for (int j=0;j<3;j++) quads[quadCount][i][j]=vertex[j];
			}
			quadCount++;
		}
	}
	
	public double distance(double vec1[],double vec2[])
	{
		return Math.sqrt(Math.pow(vec1[0]-vec2[0],2)+
				Math.pow(vec1[1]-vec2[1],2)+
				Math.pow(vec1[2]-vec2[2],2));
	}
	
	public double[] addVectors(double vec1[], double vec2[], double bias)
	{
		double returnVal[]={vec1[0]+vec2[0]*bias, vec1[1]+vec2[1]*bias, vec1[2]+vec2[2]*bias};
		return returnVal;
	}
	
	public double[] createVertex(double coordX, double coordY, double coordZ)
	{
		double returnVal[]={coordX,coordY,coordZ};
		return returnVal;
	}
}
